#ifndef _AUX_YATE_HAL_H_
#define _AUX_YATE_HAL_H_


#include <time.h>
#include <cstdlib>
#include <cstring>
#include <list>
#include <vector>
#include <map>
#include <set>
#include <string>
#include <stdio.h>
#include <stdlib.h>
#include <thread>
#include <functional>

#include <sstream>
#include <memory>


#ifdef _MSC_VER
#include <WinSock2.h>
#include <ws2ipdef.h>
#include <WS2tcpip.h>
#endif

#include <assert.h>

#include "Aux_type.h"
#include "Aux_yate_log.h"

#include <yateclass.h>
#include <yatephone.h>
//#include <yatecbase.h>


#define TE TelEngine


#define ShowLevel TelEngine::DebugNote

void YATE_API show_data(void * data, int size);
void YATE_API show_data_x(void * data, int size);

bool YATE_API b64_decode(const char * in, TelEngine::DataBlock& dest);
bool YATE_API b64_decode(void * in, int size, TelEngine::DataBlock& dest);

void YATE_API logStartup();
void YATE_API logShutdown();

class YATE_API Aux_cmd_line
{
public:
	Aux_cmd_line(void);
	~Aux_cmd_line(void);

public:

	int m_argc;
	char ** m_argv;

	void clear();

	int set(int argc, char ** argv);

	int format(const char * cmd);

protected:

};


extern unsigned long long withDebugSelector;
extern TelEngine::DebugEnabler* withDebugEnabler();

//=================================================================================================
//=================================================================================================
//=================================================================================================


#ifdef _DISABLE_YATE_SDP_LOG_

#define Y_TRACE(message) do { \
}while(0)

#define Y_DEBUG(message) do { \
}while(0)

#define Y_INFO(message) do { \
}while(0)

#define Y_WARN(message) do { \
}while(0)

#define Y_ERROR(message) do { \
}while(0)

#define Y_FATAL(message) do { \
}while(0)


#define YP_TRACE(message) do { \
}while(0)

#define YP_DEBUG(message) do { \
}while(0)

#define YP_INFO(message) do { \
}while(0)

#define YP_WARN(message) do { \
}while(0)

#define YP_ERROR(message) do { \
}while(0)

#define YP_FATAL(message) do { \
}while(0)


#define YF_TRACE(message) do { \
}while(0)

#define YF_DEBUG(message) do { \
}while(0)

#define YF_INFO(message) do { \
}while(0)

#define YF_WARN(message) do { \
}while(0)

#define YF_ERROR(message) do { \
}while(0)

#define YF_FATAL(message) do { \
}while(0)


#define X_TRACE(x, message) do { \
}while(0)

#define X_DEBUG(x, message) do { \
}while(0)

#define X_INFO(x, message) do { \
}while(0)

#define X_WARN(x, message) do { \
}while(0)

#define X_ERROR(x, message) do { \
}while(0)

#define X_FATAL(x, message) do { \
}while(0)


#define XP_TRACE(x, message) do { \
}while(0)

#define XP_DEBUG(x, message) do { \
}while(0)

#define XP_INFO(x, message) do { \
}while(0)

#define XP_WARN(x, message) do { \
}while(0)

#define XP_ERROR(x, message) do { \
}while(0)

#define XP_FATAL(x, message) do { \
}while(0)

#define XF_TRACE(x, message) do { \
}while(0)

#define XF_DEBUG(x, message) do { \
}while(0)

#define XF_INFO(x, message) do { \
}while(0)

#define XF_WARN(x, message) do { \
}while(0)

#define XF_ERROR(x, message) do { \
}while(0)

#define XF_FATAL(x, message) do { \
}while(0)



#else

/*
* withDebugEnabler() need to define in module
*
*/


#define LOG_Y_TRACE(message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_DEBUG(message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_INFO(message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_WARN(message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_ERROR(message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_FATAL(message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << message; \
		} \
	}	\
}while(0)


#define LOG_Y_TRACE_IF(condition,message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_DEBUG_IF(condition,message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_INFO_IF(condition,message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_WARN_IF(condition,message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_ERROR_IF(condition,message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if((condition) &&  d ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_Y_FATAL_IF(condition,message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << message; \
		} \
	}	\
}while(0)


#define LOG_YP_TRACE(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_DEBUG(message,...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_INFO(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_WARN(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_ERROR(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_FATAL(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)


#define LOG_YP_TRACE_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_DEBUG_IF(condition,message,...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_INFO_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_WARN_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_ERROR_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YP_FATAL_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)


#define LOG_YF_TRACE(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_DEBUG(message,...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_INFO(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_WARN(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_ERROR(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_FATAL(message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)


#define LOG_YF_TRACE_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_DEBUG_IF(condition,message,...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_INFO_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_WARN_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_ERROR_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_YF_FATAL_IF(condition,message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( (condition) && d ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)


//=================================================================================================
//=================================================================================================
//=================================================================================================

/*
* 
*  logger selector -> withDebugSelector
*  need to define in module
* 
*/

#define LOG_X_TRACE(x, message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_trace(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_X_DEBUG(x, message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_debug(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_X_INFO(x, message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_info(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_X_WARN(x, message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_warn(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_X_ERROR(x, message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_error(d->debugName()) << message; \
		} \
	}	\
}while(0)

#define LOG_X_FATAL(x, message) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_error(d->debugName()) << message; \
		} \
	}	\
}while(0)


#define LOG_XP_TRACE(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XP_DEBUG(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XP_INFO(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XP_WARN(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XP_ERROR(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XP_FATAL(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << fmt::sprintf(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)


#define LOG_XF_TRACE(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() > TelEngine::DebugAll){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XF_DEBUG(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugAll){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XF_INFO(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugInfo){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XF_WARN(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugWarn){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XF_ERROR(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugCrit){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)

#define LOG_XF_FATAL(x, message, ...) do { \
	TelEngine::DebugEnabler * d = withDebugEnabler(); \
	if( d && ((x)&withDebugSelector) ){		\
		if( d->debugLevel() >= TelEngine::DebugFail){ \
			yate_h_stream_fatal(d->debugName()) << fmt::format(message, ##__VA_ARGS__); \
		} \
	}	\
}while(0)



#define Y_TRACE	   LOG_Y_TRACE
#define Y_DEBUG	   LOG_Y_DEBUG
#define Y_INFO	   LOG_Y_INFO
#define Y_WARN	   LOG_Y_WARN
#define Y_ERROR	   LOG_Y_ERROR
#define Y_FATAL	   LOG_Y_FATAL


#endif

//=================================================================================================
//=================================================================================================
//=================================================================================================



#ifndef ANY_REF_OBJECT_TYPE_NAME

#define ANY_REF_OBJECT_TYPE_NAME "AnyRefObject"

namespace TelEngine {

	// A variant type that can hold any other type.
	//
	// Usage 1 :
	//
	//    Buffer* buf(new Buffer());
	//    Any any(buf);
	//    Buffer* b = any_cast<Buffer*>(any);
	//    assert(buf == b);
	//    delete buf;
	//
	//
	// Usage 2 :
	//
	//    std::shared_ptr<Buffer> buf(new Buffer());
	//    Any any(buf);
	//    std::shared_ptr<Buffer> b = any_cast<std::shared_ptr<Buffer>>(any);
	//    assert(buf.get() == b.get());
	//
	//
	// Usage 3 :
	//
	//    std::shared_ptr<Buffer> buf(new Buffer());
	//    Any any(buf);
	//    std::shared_ptr<Buffer> b = any.Get<std::shared_ptr<Buffer>>();
	//    assert(buf.get() == b.get());
	//
	class Any {

	public:
		Any() : content_(nullptr) {}
		~Any() {
			delete content_;
		}

		template<typename ValueType>
		explicit Any(const ValueType& value)
			: content_(new Holder<ValueType>(value)) {}

		Any(const Any& other)
			: content_(other.content_ ? other.content_->clone() : nullptr) {}

	public:
		Any& swap(Any& rhs) {
			std::swap(content_, rhs.content_);
			return*this;
		}

		template<typename ValueType>
		Any& operator=(const ValueType& rhs) {
			Any(rhs).swap(*this);
			return*this;
		}

		Any& operator=(const Any& rhs) {
			Any(rhs).swap(*this);
			return*this;
		}

		bool IsEmpty() const {
			return !content_;
		}

		const std::type_info& GetType() const {
			return content_ ? content_->GetType() : typeid(void);
		}

		template<typename ValueType>
		ValueType operator()() const {
			return Get<ValueType>();
		}

		template<typename ValueType>
		ValueType Get() const {
			if (GetType() == typeid(ValueType)) {
				return static_cast<Any::Holder<ValueType>*>(content_)->held_;
			}
			else {
				return ValueType();
			}
		}

	protected:

		class PlaceHolder {
		public:
			virtual ~PlaceHolder() {}
		public:
			virtual const std::type_info& GetType() const = 0;
			virtual PlaceHolder* clone() const = 0;
		};

		template<typename ValueType>
		class Holder : public PlaceHolder {
		public:
			Holder(const ValueType& value)
				: held_(value) {}

			virtual const std::type_info& GetType() const {
				return typeid(ValueType);
			}

			virtual PlaceHolder* clone() const {
				return new Holder(held_);
			}

			ValueType held_;
		};

	protected:
		PlaceHolder* content_;
		template<typename ValueType>
		friend ValueType* any_cast(Any*);
	};

	template<typename ValueType>
	ValueType* any_cast(Any* any) {
		if (any && any->GetType() == typeid(ValueType)) {
			return &(static_cast<Any::Holder<ValueType>*>(any->content_)->held_);
		}

		return nullptr;
	}

	template<typename ValueType>
	const ValueType* any_cast(const Any* any) {
		return any_cast<ValueType>(const_cast<Any*>(any));
	}

	template<typename ValueType>
	ValueType any_cast(const Any& any) {
		const ValueType* result = any_cast<ValueType>(&any);
		//assert(result);

		if (!result) {
			return ValueType();
		}

		return *result;
	}



	static const int64_t kNanosecond = 1LL;
	static const int64_t kMicrosecond = 1000 * kNanosecond;
	static const int64_t kMillisecond = 1000 * kMicrosecond;
	static const int64_t kSecond = 1000 * kMillisecond;
	static const int64_t kMinute = 60 * kSecond;
	static const int64_t kHour = 60 * kMinute;

	// A Duration represents the elapsed time between two instants
	// as an int64 nanosecond count. The representation limits the
	// largest representable duration to approximately 290 years.
	class Duration {
	public:
		//static const int64_t kNanosecond; // = 1LL
		//static const int64_t kMicrosecond;// = 1000
		//static const int64_t kMillisecond;// = 1000 * kMicrosecond
		//static const int64_t kSecond; // = 1000 * kMillisecond
		//static const int64_t kMinute; // = 60 * kSecond
		//static const int64_t kHour;   // = 60 * kMinute
	public:
		Duration();
		explicit Duration(const struct timeval& t);
		explicit Duration(int64_t nanoseconds);
		explicit Duration(int nanoseconds);
		explicit Duration(double seconds);

		// Nanoseconds returns the duration as an integer nanosecond count.
		int64_t Nanoseconds() const;

		// These methods return double because the dominant
		// use case is for printing a floating point number like 1.5s, and
		// a truncation to integer would make them not useful in those cases.

		// Seconds returns the duration as a floating point number of seconds.
		double Seconds() const;

		double Milliseconds() const;
		double Microseconds() const;
		double Minutes() const;
		double Hours() const;

		struct timeval TimeVal() const;
		void To(struct timeval* t) const;

		bool IsZero() const;
		bool operator< (const Duration& rhs) const;
		bool operator<=(const Duration& rhs) const;
		bool operator> (const Duration& rhs) const;
		bool operator>=(const Duration& rhs) const;
		bool operator==(const Duration& rhs) const;

		Duration operator+=(const Duration& rhs);
		Duration operator-=(const Duration& rhs);
		Duration operator*=(int ns);
		Duration operator/=(int ns);

	private:
		int64_t ns_; // nanoseconds
	};


	inline Duration::Duration()
		: ns_(0) {}

	inline Duration::Duration(const struct timeval& t)
		: ns_(t.tv_sec* kSecond + t.tv_usec * kMicrosecond) {}

	inline Duration::Duration(int64_t nanoseconds)
		: ns_(nanoseconds) {}

	inline Duration::Duration(int nanoseconds)
		: ns_(nanoseconds) {}

	inline Duration::Duration(double seconds)
		: ns_((int64_t)(seconds* kSecond)) {}

	inline int64_t Duration::Nanoseconds() const {
		return ns_;
	}

	inline double Duration::Seconds() const {
		return double(ns_) / kSecond;
	}

	inline double Duration::Milliseconds() const {
		return double(ns_) / kMillisecond;
	}

	inline double Duration::Microseconds() const {
		return double(ns_) / kMicrosecond;
	}

	inline double Duration::Minutes() const {
		return double(ns_) / kMinute;
	}

	inline double Duration::Hours() const {
		return double(ns_) / kHour;
	}

	inline bool Duration::IsZero() const {
		return ns_ == 0;
	}

	inline struct timeval Duration::TimeVal() const {
		struct timeval t;
		To(&t);
		return t;
	}

	inline void Duration::To(struct timeval* t) const {
		t->tv_sec = (long)(ns_ / kSecond);
		t->tv_usec = (long)(ns_ % kSecond) / (long)kMicrosecond;
	}

	inline bool Duration::operator<(const Duration& rhs) const {
		return ns_ < rhs.ns_;
	}

	inline bool Duration::operator<=(const Duration& rhs) const {
		return ns_ <= rhs.ns_;
	}

	inline bool Duration::operator>(const Duration& rhs) const {
		return ns_ > rhs.ns_;
	}

	inline bool Duration::operator>=(const Duration& rhs) const {
		return ns_ >= rhs.ns_;
	}

	inline bool Duration::operator==(const Duration& rhs) const {
		return ns_ == rhs.ns_;
	}

	inline Duration Duration::operator+=(const Duration& rhs) {
		ns_ += rhs.ns_;
		return *this;
	}

	inline Duration Duration::operator-=(const Duration& rhs) {
		ns_ -= rhs.ns_;
		return *this;
	}

	inline Duration Duration::operator*=(int n) {
		ns_ *= n;
		return *this;
	}

	inline Duration Duration::operator/=(int n) {
		ns_ /= n;
		return *this;
	}

	class EventLoop;
	class Timer;

	typedef std::shared_ptr<Timer> TimerPtr;

	class Timer : public std::enable_shared_from_this<Timer> {
	public:
		typedef std::function<void()> Functor;

		virtual ~Timer() = default;

		// It is thread safe.
		// Start this timer.
		virtual void Start() = 0;

		// Cancel the timer and the cancel_callback_ will be invoked.
		virtual void Cancel() = 0;

		virtual void set_cancel_callback(const Functor& fn) = 0;

	};


	typedef std::shared_ptr<EventLoop> EventLoopPtr;

	class EventLoop {

	public:

		typedef std::function<void()> Functor;

	public:

		virtual ~EventLoop() = default;

		virtual TimerPtr RunAfter(Duration delay, const Functor& f) = 0;
		TimerPtr RunAfter(double delay_ms, const Functor& f) {
			return RunAfter(Duration(delay_ms / 1000), f);
		}

		// RunEvery executes Functor f every period interval time.
		virtual TimerPtr RunEvery(Duration interval, const Functor& f) = 0;
		TimerPtr RunEvery(double interval_ms, const Functor& f) {
			return RunEvery(Duration(interval_ms / 1000), f);
		}

		//======================================================================

		virtual TimerPtr RunAfter(Duration delay, Functor&& f) = 0;
		TimerPtr RunAfter(double delay_ms, Functor&& f) {
			return RunAfter(Duration(delay_ms / 1000), f);
		}

		// RunEvery executes Functor f every period interval time.
		virtual TimerPtr RunEvery(Duration interval, Functor&& f) = 0;
		TimerPtr RunEvery(double interval_ms, Functor&& f) {
			return RunEvery(Duration(interval_ms / 1000), f);
		}

		//======================================================================

		virtual void RunInLoop(const Functor& handler) = 0;
		virtual void QueueInLoop(const Functor& handler) = 0;

		virtual void RunInLoop(Functor&& handler) = 0;
		virtual void QueueInLoop(Functor&& handler) = 0;

		virtual size_t GetPendingQueueSize() = 0;
		virtual bool IsPendingQueueEmpty() = 0;

		virtual bool IsInLoopThread() const = 0;

		virtual bool IsRunning() const = 0;

		virtual const std::thread::id& tid() const = 0;

		virtual int load() = 0;

		void set_context(const Any& c) {
			context_[0] = c;
		}
		const Any& context() const {
			return context_[0];
		}
		void set_context(int index, const Any& c) {
			assert(index < kContextCount && index >= 0);
			context_[index] = c;
		}
		const Any& context(int index) const {
			assert(index < kContextCount && index >= 0);
			return context_[index];
		}

		//======================================================================

		// @brief Stop the event loop
		virtual void Stop() = 0;

	protected:

		// @brief Run the IO Event driving loop forever
		// @note It must be called in the IO Event thread
		virtual void Run() = 0;
		enum { kContextCount = 16, };
		Any context_[kContextCount];
	};

	class RxEventApi : public std::enable_shared_from_this<RxEventApi> {

	public:
		virtual ~RxEventApi() = default;
		virtual const std::string& get_name() = 0;
		virtual const std::string& get_version() = 0;
		virtual EventLoop* get_main_loop_np() = 0;
		virtual const std::shared_ptr< EventLoop >& get_main_loop() = 0;
		virtual const std::vector< EventLoopPtr >& get_loop_pool() = 0;
		virtual const std::shared_ptr< EventLoop >& get_task_loop(const std::string& task) = 0;
		virtual const std::vector< EventLoopPtr >& get_task_thread_pool(const std::string& task) = 0;

	};





} // END of 	namespace TelEngine {


//=================================================================================================
//=================================================================================================
//=================================================================================================


template<class T>
class AnyRefObject : public TelEngine::RefObject {

	enum anytype {
		any_shared_ptr,
		any_native_ptr
	};

public:

	virtual void* getObject(const TelEngine::String& name) const {
		if (name == ANY_REF_OBJECT_TYPE_NAME) {
			return (void*)this;
		}
		return RefObject::getObject(name);
	}

	const char* getTemplateName() {
		return typeid(_my_template).name();
	}

	const TelEngine::String& getSaveTemplateName() {
		return _save_template_name;
	}

	bool isCastValidate() {
		return _save_template_name == getTemplateName();
	}

	void set(T* t, bool withname = true) {
		_anytype = any_native_ptr;
		_any = TelEngine::Any(t);
		if (withname) {
			_save_template_name = getTemplateName();
		}
		_with_name = withname;
	}

	void set(const std::shared_ptr<T>& t, bool withname = true) {
		_anytype = any_shared_ptr;
		_any = TelEngine::Any(t);
		if (withname) {
			_save_template_name = getTemplateName();
		}
		_with_name = withname;
	}

	T* get_native() {
		if (_anytype != anytype::any_native_ptr) {
			return nullptr;
		}
		return TelEngine::any_cast<T*>(_any);
	}

	std::shared_ptr<T> get_shared() {
		if (_anytype != anytype::any_shared_ptr) {
			return nullptr;
		}
		return _any.Get< std::shared_ptr<T> >();
	}

	bool _with_name = false;

	T* _my_template = 0;

protected:

	anytype _anytype;

	TelEngine::Any _any;

	TelEngine::String _save_template_name;


};

template<class T>
T* AnyRefObject_np(const TelEngine::Message & msg) {

	TelEngine::RefObject* ref_object = msg.userData();
	if (!ref_object) {
		return nullptr;
	}
	void* userData = ref_object->getObject(ANY_REF_OBJECT_TYPE_NAME);
	if (!userData) {
		return nullptr;
	}
	AnyRefObject<T>* user_data = (AnyRefObject<T> *)userData;
	if (!user_data->isCastValidate()) {
		return nullptr;
	}
	return user_data->get_native();
}

template<class T>
std::shared_ptr<T> AnyRefObject_sp(const TelEngine::Message & msg) {

	TelEngine::RefObject* ref_object = msg.userData();
	if (!ref_object) {
		return nullptr;
	}
	void* userData = ref_object->getObject(ANY_REF_OBJECT_TYPE_NAME);
	if (!userData) {
		return nullptr;
	}
	AnyRefObject<T>* user_data = (AnyRefObject<T> *)userData;
	if (!user_data->isCastValidate()) {
		return nullptr;
	}
	return user_data->get_shared();
}

class YateThread : public TelEngine::Thread
{
public:
	YateThread(const char* name) : TelEngine::Thread(name) {}
	virtual void run() {
		if (_fun) { _fun(); }
	}
	virtual void cleanup() {
		if (_clean) { _clean(); }
	}
	void set_fun(const std::function<void()>& f) {
		_fun = f;
	}
	void set_fun(std::function<void()>&& f) {
		_fun = std::move(f);
	}
	void set_clean(const std::function<void()>& c) {
		_clean = c;
	}
	void set_clean(std::function<void()>&& c) {
		_clean = std::move(c);
	}
public:
	std::function<void()> _fun;
	std::function<void()> _clean;
};

class YateMsgHandler : public TelEngine::MessageHandler
{
public:

	YateMsgHandler(const char* name, unsigned priority = 100,
		const char* trackName = 0, bool addPriority = true) :
		TelEngine::MessageHandler(name, priority, trackName, addPriority) {
	}

	virtual bool received(TelEngine::Message& msg) {
		if (_handler) {
			return _handler(msg);
		}
		return false;
	}

public:
	std::function< bool(TelEngine::Message& msg)> _handler;

};

inline int jni_cbsub_dispatch(int what, int arg1, int arg2, std::string & cmd, std::string & result)
{
	TelEngine::Message msg("Callback.cmd");
	msg.setParam("Op", "dispatch");
	msg.setParam("what", TelEngine::String(what));
	msg.setParam("arg1", TelEngine::String(arg1));
	msg.setParam("arg2", TelEngine::String(arg2));
	if (cmd.length() > 0) {
		msg.setParam("cmd", cmd.c_str());
	}
	if (TelEngine::Engine::dispatch(msg)) {
		if (msg.retValue().length()) {
			result = msg.retValue().safe();
		}
		return 0;
	}
	TelEngine::Debug(ShowLevel, "JNI_callback_sub error");
	return -1;
}

inline int jni_cbsub_dispatch(int what, int arg1, int arg2, std::string & result)
{
	std::string cmd;
	return jni_cbsub_dispatch(what, arg1, arg2, result);
}

inline int jni_cbsub_dispatch(int what, int arg1, std::string & result)
{
	std::string cmd;
	return jni_cbsub_dispatch(what, arg1, 0, result);
}

inline void jni_cbsub_enqueue(int what, int arg1, int arg2, std::string & cmd)
{
	TelEngine::Message* msg = new TelEngine::Message("Callback.cmd");
	msg->setParam("Op", "enqueue");
	msg->setParam("what", TelEngine::String(what));
	msg->setParam("arg1", TelEngine::String(arg1));
	msg->setParam("arg2", TelEngine::String(arg2));
	if (cmd.length() > 0) {
		msg->setParam("cmd", cmd.c_str());
	}
	TelEngine::Engine::enqueue(msg);
}

inline void jni_cbsub_enqueue(int what, int arg1, int arg2)
{
	std::string cmd;
	jni_cbsub_enqueue(what, arg1, arg2, cmd);
}

inline void jni_cbsub_enqueue(int what, int arg1)
{
	std::string cmd;
	jni_cbsub_enqueue(what, arg1, 0, cmd);
}

inline std::string y_base64_enc(void* in, int size)
{
	TelEngine::Base64 b(in, size);
	TelEngine::String str;
	b.encode(str);
	return std::string(str.safe());
}


inline bool y_base64_dec(const char* b64, TelEngine::DataBlock & dest)
{
	TelEngine::Base64 b((void*)b64, (int)strlen(b64));
	return b.decode(dest);
}


inline bool y_base64_dec(void* b64, int b64_len, TelEngine::DataBlock & dest)
{
	TelEngine::Base64 b((void*)b64, b64_len);
	return b.decode(dest);
}


inline std::string y_hexify(void* in, int size)
{
	TelEngine::String str;
	str.hexify(in, size);
	return std::string(str.safe());
}

inline U16 CalcCheckSum(const U8 * data, int size)
{
	int i, num;
	unsigned int checksum, v;
	checksum = 0; num = size >> 1;
	for (i = 0; i < num; i++) {
		v = (data[i << 1] << 8) + data[(i << 1) + 1];
		checksum += v;
	}
	if (size % 2) {
		v = data[size - 1] << 8;
		checksum += v;
	}
	checksum = (checksum >> 16) + (checksum & 0xffff);
	while (checksum >= 0x10000) {
		checksum = (checksum >> 16) + (checksum & 0xffff);
	}
	return (U16)(~checksum);
}

inline void string_replace(std::string & strBig, const std::string & strsrc, const std::string & strdst)
{
	std::string::size_type pos = 0;
	std::string::size_type srclen = strsrc.size();
	std::string::size_type dstlen = strdst.size();

	while ((pos = strBig.find(strsrc, pos)) != std::string::npos) {
		strBig.replace(pos, srclen, strdst);
		pos += dstlen;
	}
}

inline void stringSplit(const char* str, char separator, std::function< void(int count, int index, TelEngine::String * s)> hanler)
{
	TelEngine::String _str(str);
	//_str.split(separator);
	TelEngine::ObjList* l = _str.split(separator);
	if (l) {
		int index = 0;
		int c = l->count();
		for (TelEngine::ObjList* p = l; p; p = p->next(), index++) {
			TelEngine::String* s = static_cast<TelEngine::String*>(p->get());
			if (hanler) {
				hanler(c, index, s);
			}
		}
		l->destruct();
	}

}

inline void stringSplit(const char* str, char separator, bool emptyOK, std::function< void(int count, int index, TelEngine::String * s)> hanler)
{
	TelEngine::String _str(str);
	//_str.split(separator);
	TelEngine::ObjList* l = _str.split(separator, emptyOK);
	if (l) {
		int index = 0;
		int c = l->count();
		for (TelEngine::ObjList* p = l; p; p = p->next(), index++) {
			TelEngine::String* s = static_cast<TelEngine::String*>(p->get());
			if (hanler) {
				hanler(c, index, s);
			}
		}
		l->destruct();
	}
}


#endif // END of #ifndef ANY_REF_OBJECT_TYPE_NAME

#endif // END of #ifndef _AUX_YATE_HAL_H_
